package com.ruoyi.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.domain.DailyStat;
import com.ruoyi.domain.GnssData;
import com.ruoyi.domain.Sensor;
import com.ruoyi.service.ISensorService;
import com.ruoyi.util.HttpClientUtil;
import lombok.SneakyThrows;
import org.apache.http.Header;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.BiFunction;

@RestController
@RequestMapping()
public class GNSSController {

    @Autowired
    private ISensorService sensorService;

    /** 给前端输出的时间格式 */
    private static final DateTimeFormatter OUT_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @GetMapping("/getGNSSData")
    public AjaxResult getGNSSData() {
        return AjaxResult.success(fetchAndCalc());
    }

    @SneakyThrows
    private List<GnssData> fetchAndCalc() {
        List<Sensor> sensors = sensorService.selectSensorList(new Sensor());

        // 当前时刻 & 7 天前（用于 API 参数）
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime sevenDaysAgo = now.minusDays(7);
        String startTime = URLEncoder.encode(
                sevenDaysAgo.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
                StandardCharsets.UTF_8.name()
        ).replace("+", "%20");
        String endTime = URLEncoder.encode(
                now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
                StandardCharsets.UTF_8.name()
        ).replace("+", "%20");

        List<GnssData> result = new ArrayList<>();

        for (Sensor sensor : sensors) {
            // 1. 调用历史数据接口
            String url = String.format(
                    "https://iot.smos.southgnss.com/api/devices/datapoints/historical" +
                            "?mqttDeviceId=%s" +
                            "&mqttSensorCode=%s" +
                            "&startTime=%s" +
                            "&endTime=%s",
                    URLEncoder.encode(sensor.getMqttDeviceId(), "UTF-8"),
                    URLEncoder.encode(sensor.getMqttSensorCode(), "UTF-8"),
                    startTime,
                    endTime
            );
            String resp = HttpClientUtil.doGet(url, new HashMap<>(), new Header[]{});

            // 2. 解析 JSON，抽出每个时间点的 gpsTotalZ
            JSONObject root = JSON.parseObject(resp);
            JSONObject data = root.getJSONObject("data");
            if (data == null) continue;
            JSONObject devObj = data.getJSONObject(sensor.getMqttDeviceId());
            if (devObj == null) continue;
            JSONObject senObj = devObj.getJSONObject(sensor.getMqttSensorCode());
            if (senObj == null) continue;

            TreeMap<LocalDateTime, Double> zMap = new TreeMap<>();
            for (String tstr : senObj.keySet()) {
                LocalDateTime t = LocalDateTime.ofInstant(Instant.parse(tstr), ZoneOffset.UTC);
                double z = senObj.getJSONObject(tstr).getDoubleValue("gpsTotalZ");
                zMap.put(t, z);
            }
            if (zMap.isEmpty()) continue;

            // 最新点
            Map.Entry<LocalDateTime, Double> latest = zMap.lastEntry();
            LocalDateTime tNow = latest.getKey();
            double zNow = latest.getValue();

            // 3. 查过去 N 天的参考点，若不足 N 天则 fallback 到最早一条
            BiFunction<Long, Void, Map.Entry<LocalDateTime, Double>> findPast = (d, v) -> {
                Map.Entry<LocalDateTime, Double> e = zMap.floorEntry(tNow.minusDays(d));
                return e != null ? e : zMap.firstEntry();
            };

            GnssData vo = new GnssData();
            vo.setDeviceId(sensor.getMqttDeviceId());
            vo.setSensorCode(sensor.getMqttSensorCode());
            vo.setMqttName(sensor.getMqttName());
            vo.setCurrentTime(now.format(OUT_FMT));

            long[] daysArr = {1, 2, 3, 7};
            for (long d : daysArr) {
                Map.Entry<LocalDateTime, Double> past = findPast.apply(d, null);
                double delta = zNow - past.getValue();           // 有向下沉值
                double speed = Math.abs(delta) / d;              // mm/day（正值）
                switch ((int) d) {
                    case 1:
                        vo.setChange1d(delta);
                        vo.setSpeed1d(speed);
                        break;
                    case 2:
                        vo.setChange2d(delta);
                        vo.setSpeed2d(speed);
                        break;
                    case 3:
                        vo.setChange3d(delta);
                        vo.setSpeed3d(speed);
                        break;
                    case 7:
                        vo.setChange7d(delta);
                        vo.setSpeed7d(speed);
                        break;
                }
            }

            // 4. 构造过去 7 天每天的四指标
            List<DailyStat> stats = new ArrayList<>(7);
            for (int i = 1; i <= 7; i++) {
                DailyStat ds = new DailyStat();
                // 今天到 i 天前的 z 值
                double zi = findPast.apply((long) i, null).getValue();
                // 前一天到 (i-1) 天前的 z 值（i==1 则用最新值）
                double zPrev = (i == 1
                        ? zNow
                        : findPast.apply((long) (i - 1), null).getValue()
                );

                // 日下沉速度
                ds.setDailySpeed(Math.abs(zPrev - zi));
                // 累计下沉量
                ds.setCumulativeChange(Math.abs(zNow - zi));
                // 2 天速
                if (i >= 2) {
                    double z2 = findPast.apply((long) (i - 2), null).getValue();
                    ds.setTwoDaySpeed(Math.abs(z2 - zi) / 2.0);
                }
                // 3 日速
                if (i >= 3) {
                    double z3 = findPast.apply((long) (i - 3), null).getValue();
                    ds.setThreeDaySpeed(Math.abs(z3 - zi) / 3.0);
                }
                stats.add(ds);
            }
            vo.setStatsList(stats);

            result.add(vo);
        }

        return result;
    }
}
